﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Drawing;
using HalconDotNet;
using Go;

namespace fhs
{
    static class AOI
    {
        static public tuple<MyPoint, MyPoint, MyPoint, MyPoint> CalcROIDots(Roi.Rect roi, MyPoint erosion = default(MyPoint), HTuple leftHomMat = null, HTuple rightHomMat = null)
        {
            HTuple homMat, rows, cols;
            if (null != rightHomMat)
            {
                homMat = rightHomMat;
            }
            else
            {
                HOperatorSet.HomMat2dIdentity(out homMat);
            }
            HOperatorSet.HomMat2dRotate(homMat, roi.Phi, roi.Row, roi.Col, out homMat);
            if (null != leftHomMat)
            {
                HOperatorSet.HomMat2dCompose(leftHomMat, homMat, out homMat);
            }
            HOperatorSet.AffineTransPoint2d(homMat,
                new HTuple(roi.Row - (roi.Len2 - erosion.Row), roi.Row - (roi.Len2 - erosion.Row), roi.Row + (roi.Len2 - erosion.Row), roi.Row + (roi.Len2 - erosion.Row)),
                new HTuple(roi.Col + (roi.Len1 - erosion.Col), roi.Col - (roi.Len1 - erosion.Col), roi.Col - (roi.Len1 - erosion.Col), roi.Col + (roi.Len1 - erosion.Col)), out rows, out cols);
            return tuple.make(new MyPoint { Row = rows[0], Col = cols[0] }, new MyPoint { Row = rows[1], Col = cols[1] }, new MyPoint { Row = rows[2], Col = cols[2] }, new MyPoint { Row = rows[3], Col = cols[3] });
        }

        public struct FindLineResult
        {
            public tuple<MyPoint, MyPoint> line;
            public HObject roi;
            public HObject lineContour;
            public HObject measCross;
            public double fitScore;
        }

        static public FindLineResult FindLine2(HObject Image, Roi.Rect roi, int measCount, double sigma, int threshold, bool transition, double fitGap = 0)
        {
            FindLineResult result = new FindLineResult();
            HOperatorSet.GenRectangle2ContourXld(out result.roi, roi.Row, roi.Col, roi.Phi, roi.Len1, roi.Len2);
            HTuple homMat, startRow, startCol;
            HOperatorSet.HomMat2dIdentity(out homMat);
            HOperatorSet.HomMat2dRotate(homMat, roi.Phi, roi.Row, roi.Col, out homMat);
            HOperatorSet.AffineTransPoint2d(homMat, roi.Row - roi.Len2, roi.Col - roi.Len1, out startRow, out startCol);
            HObject startCross;
            HOperatorSet.GenCrossContourXld(out startCross, startRow, startCol, 60, FlatCalib.ToRad(45));
            HOperatorSet.ConcatObj(result.roi, startCross, out result.roi);
            HTuple width, height, metModel, metIndex;
            HOperatorSet.GetImageSize(Image, out width, out height);
            HOperatorSet.CreateMetrologyModel(out metModel);
            try
            {
                HOperatorSet.SetMetrologyModelImageSize(metModel, width, height);
                MyPoint o = new MyPoint { Row = roi.Row, Col = roi.Col };
                MyPoint begin = FlatCalib.RotatePoint(o, new MyPoint { Row = roi.Row, Col = roi.Col - roi.Len1 }, -roi.Phi);
                MyPoint end = FlatCalib.RotatePoint(o, new MyPoint { Row = roi.Row, Col = roi.Col + roi.Len1 }, -roi.Phi);
                HOperatorSet.AddMetrologyObjectLineMeasure(metModel, begin.Row, begin.Col, end.Row, end.Col,
                    roi.Len2, roi.Len1 / measCount, sigma, threshold,
                    new HTuple("measure_transition", "min_score", "measure_interpolation", "measure_distance"),
                    new HTuple(transition ? "positive" : "negative", 0, "bilinear", 2 * roi.Len1 / (measCount + 1)), out metIndex);
                HTuple beginRow, beginCol, endRow, endCol, allRows, allCols;
                HOperatorSet.ApplyMetrologyModel(Image, metModel);
                HOperatorSet.GetMetrologyObjectResult(metModel, "all", "all", "result_type", "row_begin", out beginRow);
                HOperatorSet.GetMetrologyObjectResult(metModel, "all", "all", "result_type", "column_begin", out beginCol);
                HOperatorSet.GetMetrologyObjectResult(metModel, "all", "all", "result_type", "row_end", out endRow);
                HOperatorSet.GetMetrologyObjectResult(metModel, "all", "all", "result_type", "column_end", out endCol);
                if (1 == beginRow.Length * beginCol.Length * endRow.Length * endCol.Length)
                {
                    HOperatorSet.GetMetrologyObjectResultContour(out result.lineContour, metModel, "all", "all", 1.5);
                    HOperatorSet.GetMetrologyObjectMeasures(out result.measCross, metModel, "all", "all", out allRows, out allCols);
                    HOperatorSet.GenCrossContourXld(out result.measCross, allRows, allCols, 30, FlatCalib.ToRad(45));
                    result.line = tuple.make(new MyPoint { Row = beginRow, Col = beginCol }, new MyPoint { Row = endRow, Col = endCol });
                    if (fitGap > 0)
                    {
                        HTuple fitDists;
                        HOperatorSet.DistancePl(allRows, allCols, beginRow, beginCol, endRow, endCol, out fitDists);
                        result.fitScore = fitDists.TupleLessElem(fitGap).TupleSum().D / measCount;
                        if (result.fitScore > 1)
                        {
                            result.fitScore = 1;
                        }
                    }
                }
                return result;
            }
            finally
            {
                HOperatorSet.ClearMetrologyModel(metModel);
            }
        }

        static public FindLineResult FindLine(HObject Image, Roi.Rect roi, int measCount, double sigma, int threshold, bool transition, double fitGap = 0)
        {
            FindLineResult result = new FindLineResult();
            HOperatorSet.GenRectangle2ContourXld(out result.roi, roi.Row, roi.Col, roi.Phi, roi.Len1, roi.Len2);
            HTuple homMat, startRow, startCol;
            HOperatorSet.HomMat2dIdentity(out homMat);
            HOperatorSet.HomMat2dRotate(homMat, roi.Phi, roi.Row, roi.Col, out homMat);
            HOperatorSet.AffineTransPoint2d(homMat, roi.Row - roi.Len2, roi.Col - roi.Len1, out startRow, out startCol);
            HObject startCross;
            HOperatorSet.GenCrossContourXld(out startCross, startRow, startCol, 60, FlatCalib.ToRad(45));
            HOperatorSet.ConcatObj(result.roi, startCross, out result.roi);
            HTuple width, height, edgeRows = new HTuple(), edgeCols = new HTuple();
            HOperatorSet.GetImageSize(Image, out width, out height);
            double halfDist = 2 * roi.Len1 / (measCount + 1);
            for (int i = 0; i < measCount; i++)
            {
                HTuple measRow, measCol, measModel;
                HOperatorSet.AffineTransPoint2d(homMat, roi.Row, roi.Col - roi.Len1 + halfDist + i * halfDist, out measRow, out measCol);
                HOperatorSet.GenMeasureRectangle2(measRow, measCol, roi.Phi - 0.5 * Math.PI, roi.Len2, 2 * halfDist, width, height, "bilinear", out measModel);
                try
                {
                    HTuple edgeRow, edgeCol, nilAmp, nilDis;
                    HOperatorSet.MeasurePos(Image, measModel, sigma, threshold, transition ? "positive" : "negative", "first", out edgeRow, out edgeCol, out nilAmp, out nilDis);
                    edgeRows.Append(edgeRow); edgeCols.Append(edgeCol);
                }
                catch (HalconException) { }
                finally
                {
                    HOperatorSet.CloseMeasure(measModel);
                }
            }
            if (edgeRows.Length > 1)
            {
                HObject polyLine;
                HTuple fitBeginRows, fitBeginCols, fitEndRows, fitEndCols, nilFitNr, nilFitNc, nilFitDis;
                HOperatorSet.GenContourPolygonXld(out polyLine, edgeRows, edgeCols);
                HOperatorSet.FitLineContourXld(polyLine, "tukey", -1, 0, 5, 2.0, out fitBeginRows, out fitBeginCols, out fitEndRows, out fitEndCols, out nilFitNr, out nilFitNc, out nilFitDis);
                if (1 == fitBeginRows.Length)
                {
                    HOperatorSet.GenContourPolygonXld(out result.lineContour, new HTuple(fitBeginRows, fitEndRows), new HTuple(fitBeginCols, fitEndCols));
                    HOperatorSet.GenCrossContourXld(out result.measCross, edgeRows, edgeCols, 30, FlatCalib.ToRad(45));
                    result.line = tuple.make(new MyPoint { Row = fitBeginRows, Col = fitBeginCols }, new MyPoint { Row = fitEndRows, Col = fitEndCols });
                    if (fitGap > 0)
                    {
                        HTuple fitDists;
                        HOperatorSet.DistancePl(edgeRows, edgeCols, fitBeginRows, fitBeginCols, fitEndRows, fitEndCols, out fitDists);
                        result.fitScore = fitDists.TupleLessElem(fitGap).TupleSum().D / measCount;
                    }
                }
            }
            return result;
        }
    }
}
